/**
 * @file getSpecRules
 * @description getSpecRules
 * @author fanxueliang<fanxueliang
 */

import {FlatSpecType, DatasourceType} from '../interface';
import {
    keyBy,
    get,
    map,
    each,
    groupBy,
    keys,
    includes,
    filter,
    omitBy,
    set,
    cloneDeep,
    isUndefined,
    isNil,
    isBoolean
} from 'lodash-es';
import {componentUi} from './enum';
import {rulesGroup} from './rulesGroup';
import {ShallowObjectType} from '../hooks/util';
import {setNoRewriteAttrs} from './index';

const required = {
    label: '是否要求必填',
    component: componentUi.Switch,
    key: 'rulesRequired',
    path: 'rules.required',
    group: 'requiredRules'
};
const regexPattern = {
    label: '正则匹配模板',
    component: componentUi.Select,
    attr: {
        options: [],
        placeholder: '请选择匹配模板'
    },
    key: 'rulesRegexPattern',
    path: 'rules.regex.pattern',
    group: 'regexRules'
};
const regexModifier = {
    label: '匹配模式',
    component: componentUi.Select,
    attr: {
        options: [
            {
                label: '忽略大小写',
                value: 'i'
            },
            {
                label: '全局匹配',
                value: 'g'
            },
            {
                label: '忽略大小写并全局匹配',
                value: 'ig'
            }
        ],
        placeholder: '请选择匹配模式'
    },
    key: 'rulesRegexModifiers',
    path: 'rules.regex.modifiers',
    group: 'regexRules'
};
const regexErrorText = {
    label: '匹配失败提示文案',
    component: componentUi.Input,
    attr: {
        placeholder: '请填写匹配失败提示文案'
    },
    key: 'rulesRegexErrorText',
    path: 'rules.regex.errorText',
    group: 'regexRules'
};
const jsRule = {
    label: 'js验证',
    component: componentUi.Textarea,
    attr: {
        placeholder: '请填写js校验匿名函数'
    },
    key: 'rulesJsRule',
    path: 'rules.jsRule',
    group: 'jsRules'
};
const email = {
    label: '是否邮箱',
    component: componentUi.Switch,
    key: 'rulesEmail',
    path: 'rules.email',
    group: 'emailRules'
};

const emCount = {
    label: '飘红词固定个数',
    component: componentUi.NumberInput,
    key: 'rulesTagListEmCount',
    path: 'rules.tagList.em.count',
    attr: {
        min: 0,
        max: 10
    },
    group: 'tagRules'
};
const emMinCount = {
    label: '飘红词最小个数',
    component: componentUi.NumberInput,
    key: 'rulesTagListEmMinCount',
    path: 'rules.tagList.em.minCount',
    attr: {
        min: 0,
        max: 10
    },
    group: 'tagRules'
};
const emMaxCount = {
    label: '飘红词最大个数',
    component: componentUi.NumberInput,
    key: 'rulesTagListEmMaxCount',
    path: 'rules.tagList.em.maxCount',
    attr: {
        min: 0,
        max: 10
    },
    group: 'tagRules'
};

const emMinTextByteLength = {
    label: '飘红词最小字节长度',
    component: componentUi.NumberInput,
    key: 'rulesTagListEmMinTextByteLength',
    path: 'rules.tagList.em.minTextByteLength',
    attr: {
        min: 0,
        max: 100
    },
    group: 'tagRules'
};
const emMaxTextByteLength = {
    label: '飘红词最大字节长度',
    component: componentUi.NumberInput,
    key: 'rulesTagListEmMaxTextByteLength',
    path: 'rules.tagList.em.maxTextByteLength',
    attr: {
        min: 0,
        max: 100
    },
    group: 'tagRules'
};
const excludesLinkTag = {
    label: '是否排除链接',
    component: componentUi.Switch,
    key: 'rulesExcludesLinkTag',
    path: 'rules.excludesLinkTag',
    group: 'tagRules'
};

const telephone = {
    label: '是否固定电话',
    component: componentUi.Switch,
    key: 'rulesTelephone',
    path: 'rules.telephone',
    group: 'phoneRules'
};
const fax = {
    label: '是否传真号码',
    component: componentUi.Switch,
    key: 'rulesFax',
    path: 'rules.fax',
    group: 'phoneRules'
};
const mobilephone = {
    label: '是否手机号码',
    component: componentUi.Switch,
    key: 'rulesMobilephone',
    path: 'rules.mobilephone',
    group: 'phoneRules'
};
const businessPhone = {
    label: '是否企业/商业电话',
    component: componentUi.Switch,
    key: 'rulesBusinessPhone',
    path: 'rules.businessPhone',
    group: 'phoneRules'
};
const phone = {
    label: '是否电话',
    component: componentUi.Switch,
    key: 'rulesPhone',
    path: 'rules.phone',
    group: 'phoneRules'
};
const count = {
    label: '固定个数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 100
    },
    key: 'rulesCount',
    path: 'rules.count',
    group: 'countRules'
};
const minCount = {
    label: '最小个数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 100
    },
    key: 'rulesMinCount',
    path: 'rules.minCount',
    group: 'countRules'
};
const maxCount = {
    label: '最大个数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 100
    },
    key: 'rulesMaxCount',
    path: 'rules.maxCount',
    group: 'countRules'
};
const multipleCount = {
    label: '倍数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10
    },
    key: 'rulesMultipleCount',
    path: 'rules.multipleCount',
    group: 'countRules'
};
const fixLength = {
    label: '固定字符长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesFixLength',
    path: 'rules.fixLength',
    group: 'lengthRules'
};
const fixByteLength = {
    label: '固定字节长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesFixByteLength',
    path: 'rules.fixByteLength',
    group: 'lengthRules'
};
const minByteLength = {
    label: '最小字节长度(1中文=2字节)',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMinByteLength',
    path: 'rules.minByteLength',
    group: 'lengthRules'
};
const maxByteLength = {
    label: '最大字节长度(1中文=2字节)',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMaxByteLength',
    path: 'rules.maxByteLength',
    group: 'lengthRules'
};
const minTextByteLength = {
    label: '最小字节长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMinTextByteLength',
    path: 'rules.minTextByteLength',
    group: 'lengthRules'
};

const maxTextByteLength = {
    label: '最大字节长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMaxTextByteLength',
    path: 'rules.maxTextByteLength',
    group: 'lengthRules'
};
const minLength = {
    label: '最小字符长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMinLength',
    path: 'rules.minLength',
    group: 'lengthRules'
};
const maxLength = {
    label: '最大字符长度',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10000
    },
    key: 'rulesMaxLength',
    path: 'rules.maxLength',
    group: 'lengthRules'
};
const dateFormat = {
    label: '日期格式',
    component: componentUi.Input,
    attr: {
        placeholder: '默认格式：YYYY-MM-DD'
    },
    key: 'rulesDateFormat',
    path: 'rules.date.format',
    group: 'dateRules'
};
const minDate = {
    label: '早于某日期',
    component: componentUi.Input,
    attr: {
        placeholder: '默认格式：YYYY-MM-DD'
    },
    key: 'rulesDategt',
    path: 'rules.date.gt',
    group: 'dateRules'
};
const maxDate = {
    label: '晚于某日期',
    component: componentUi.Input,
    attr: {
        placeholder: '默认格式：YYYY-MM-DD'
    },
    key: 'rulesDatelt',
    path: 'rules.date.lt',
    group: 'dateRules'
};
const link = {
    label: '是否链接地址',
    component: componentUi.Switch,
    key: 'rulesLink',
    path: 'rules.link',
    group: 'urlRules'
};
const imageUrl = {
    label: '是否图片地址',
    component: componentUi.Switch,
    key: 'rulesImageUrl',
    path: 'rules.imageUrl',
    group: 'urlRules'
};
const audioUrl = {
    label: '是否音频文件地址',
    component: componentUi.Switch,
    key: 'rulesAudioUrl',
    path: 'rules.audioUrl',
    group: 'urlRules'
};
const flvUrl = {
    label: '是否flv文件地址',
    component: componentUi.Switch,
    key: 'rulesFlvUrl',
    path: 'rules.flvUrl',
    group: 'urlRules'
};
const flashUrl = {
    label: '是否flash文件地址',
    component: componentUi.Switch,
    key: 'rulesFlashUrl',
    path: 'rules.flashUrl',
    group: 'urlRules'
};
const videoUrl = {
    label: '是否视频文件地址',
    component: componentUi.Switch,
    key: 'rulesVideoUrl',
    path: 'rules.videoUrl',
    group: 'urlRules'
};
const int = {
    label: '是否整数',
    component: componentUi.Switch,
    key: 'rulesInt',
    path: 'rules.int',
    group: 'numberRules'
};
const number = {
    label: '是否数字',
    component: componentUi.Switch,
    key: 'rulesNumber',
    path: 'rules.number',
    group: 'numberRules'
};
const min = {
    label: '最小值',
    component: componentUi.NumberInput,
    key: 'rulesMinValue',
    path: 'rules.min.value',
    group: 'numberRules'
};
const max = {
    label: '最大值',
    component: componentUi.NumberInput,
    key: 'rulesMaxValue',
    path: 'rules.max.value',
    group: 'numberRules'
};
const gt = {
    label: '大于某值',
    component: componentUi.NumberInput,
    key: 'rulesGt',
    path: 'rules.gt',
    group: 'numberRules'
};
const lt = {
    label: '小于某值',
    component: componentUi.NumberInput,
    key: 'rulesLt',
    path: 'rules.lt',
    group: 'numberRules'
};
const precision = {
    label: '固定保留位数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10
    },
    key: 'rulesPrecision',
    path: 'rules.precision',
    group: 'numberRules'
};
const maxPrecision = {
    label: '最大保留位数',
    component: componentUi.NumberInput,
    attr: {
        min: 0,
        max: 10
    },
    key: 'rulesMaxPrecision',
    path: 'rules.maxPrecision',
    group: 'numberRules'
};

interface OptionsType {
    regexRules: DatasourceType[];
}

export const getSpecRules = (flatSpecItem: FlatSpecType, options: OptionsType) => {
    const {spec, modifySpec} = cloneDeep(flatSpecItem);
    const {dataType} = spec;
    if (options.regexRules.length && !regexPattern.attr.options.length) {
        (regexPattern.attr.options as DatasourceType[]) = options.regexRules;
    }
    switch (dataType) {
        case 'STRING':
            const stringRules = [
                required,
                regexPattern,
                regexModifier,
                regexErrorText,
                jsRule,
                email,
                telephone,
                fax,
                mobilephone,
                businessPhone,
                phone,
                minByteLength,
                maxByteLength,
                fixLength,
                fixByteLength,
                minLength,
                maxLength,
                dateFormat,
                minDate,
                maxDate,
                link,
                imageUrl,
                flvUrl,
                flashUrl,
                videoUrl
            ];
            set(flatSpecItem, 'specConfig.rulesList', stringRules);
            break;
        case 'COLOR':
            const colorRules = [required, regexPattern, regexModifier, regexErrorText, jsRule];
            set(flatSpecItem, 'specConfig.rulesList', colorRules);
            break;
        case 'BOOL':
            const boolRules = [required];
            set(flatSpecItem, 'specConfig.rulesList', boolRules);
            break;
        case 'NUMBER':
            const numberRules = [
                required,
                regexPattern,
                regexModifier,
                regexErrorText,
                jsRule,
                int,
                number,
                min,
                max,
                gt,
                lt,
                // precisionType,
                precision,
                maxPrecision
            ];
            set(flatSpecItem, 'specConfig.rulesList', numberRules);
            break;
        case 'UPLOAD':
            const uploadRules = [required, regexPattern, regexModifier, regexErrorText, imageUrl];
            set(flatSpecItem, 'specConfig.rulesList', uploadRules);
            break;
        case 'AUDIO':
            const audioUploadRules = [required, audioUrl];
            set(flatSpecItem, 'specConfig.rulesList', audioUploadRules);
            break;
        case 'VIDEO':
            const videoRules = [required, regexPattern, regexModifier, regexErrorText, videoUrl];
            set(flatSpecItem, 'specConfig.rulesList', videoRules);
            break;
        case 'HTML':
            const htmlRules = [
                required,
                excludesLinkTag,
                minTextByteLength,
                maxTextByteLength,
                emCount,
                emMinCount,
                emMaxCount,
                emMaxTextByteLength,
                emMinTextByteLength
            ];
            set(flatSpecItem, 'specConfig.rulesList', htmlRules);
            break;
        case 'URL':
            const urlRules = [
                required,
                regexPattern,
                regexModifier,
                regexErrorText,
                jsRule,
                link,
                imageUrl,
                flvUrl,
                flashUrl,
                videoUrl,
                maxLength
            ];
            set(flatSpecItem, 'specConfig.rulesList', urlRules);
            break;
        case 'ENUM':
            const enumRules = [required];
            set(flatSpecItem, 'specConfig.rulesList', enumRules);
            break;
        case 'OBJECT':
            const objectRules = [jsRule];
            set(flatSpecItem, 'specConfig.rulesList', objectRules);
            break;
        case 'LIST':
            const listRules = [required, jsRule, count, minCount, maxCount, multipleCount];
            set(flatSpecItem, 'specConfig.rulesList', listRules);
            break;
        case 'ALTERNATIVE':
            const alternativeRules = [required, jsRule];
            set(flatSpecItem, 'specConfig.rulesList', alternativeRules);
            break;
        default:
            throw new Error(`dataType ${dataType} is unknown!`);
    }
    let rulesList = cloneDeep(get(flatSpecItem, 'specConfig.rulesList', []));
    // 禁用不可配置项
    const noRewriteAttrs = get(spec, 'extraAttr.noRewriteAttrs', []);
    if (noRewriteAttrs.length) {
        // 禁用不可配置项
        rulesList = setNoRewriteAttrs(rulesList, noRewriteAttrs);
        set(flatSpecItem, 'specConfig.rulesList', rulesList);
    }

    const rulesMap = keyBy(rulesList, 'key');
    set(flatSpecItem, 'specConfig.rulesMap', rulesMap);
    // 构造model数据
    const rulesKeys = map(rulesList, (it) => ({key: it.key, path: it.path}));
    const rulesModel: ShallowObjectType<any> = {};
    each(rulesKeys, ({key, path}) => {
        if (!key || !path) {
            return;
        }
        let modifyValue = get(modifySpec, path);
        if (modifyValue === -1 || modifyValue === '') {
            modifyValue = null;
        }
        let value = !isUndefined(modifyValue) ? modifyValue : get(spec, path);
        if (key === 'rulesExcludesLinkTag' && !isBoolean(value)) {
            value = true;
        }
        set(rulesModel, key, path ? value : null);
    });
    set(flatSpecItem, 'specConfig.rulesModel', rulesModel);
    // 已经存在的校验规则
    const inModelKeys = keys(omitBy(rulesModel, (it) => isNil(it)));
    // 不管是否有设置默认值，都开放默认值配置
    inModelKeys.push('rulesRequired');
    const inRulesList = filter(rulesList, (it) => includes(inModelKeys, it.key));
    set(flatSpecItem, 'specConfig.inRulesList', inRulesList);

    // 要新增校验规则的分组
    const addableRulesList = filter(rulesList, (it) => !includes(inModelKeys, it.key));
    set(flatSpecItem, 'specConfig.addableRulesList', addableRulesList);
    const specRulesGroup = groupBy(addableRulesList, (it) => it.group);
    const specRulesGroupKeys = keys(specRulesGroup);
    const groupDatasource = filter(rulesGroup, (it) => includes(specRulesGroupKeys, it.value));
    set(flatSpecItem, 'specConfig.groupDatasource', groupDatasource);

    return flatSpecItem;
};
